package cn.sc.summer.construction;

import cn.hutool.core.util.StrUtil;
import cn.sc.summer.constant.project.DateConstant;
import cn.sc.summer.constant.util.DateUtilX;
import cn.sc.summer.domain.service.MinioService;
import cn.sc.summer.exception.enums.CommonExceptionEnum;
import cn.sc.summer.exception.exceptions.CommonException;
import cn.sc.summer.protocol.properties.MinioProperties;
import io.minio.MinioClient;
import io.minio.ObjectStat;
import io.minio.PutObjectOptions;
import io.minio.Result;
import io.minio.messages.DeleteError;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * 类名：minio初始化
 *
 * @author a-xin
 * @date 2024/1/29 09:21
 */
@Slf4j
@Service
public class MinioServiceImpl implements MinioService {

    @Resource
    private MinioProperties minioProperties;

    /**
     * minio初始化
     */
    @Override
    public void initMinio() {
        try {
            //检查minio桶是否存在
            if (minioProperties.isCheckBucket()) {
                if (!bucketExists()) {
                    log.info("「 {} 」 minio bucket is not exits! now create a minio bucket named 「{}」", minioProperties.getBucketName(), minioProperties.getBucketName());
                    createBucket(minioProperties.getBucketName());
                } else {
                    log.info("「 {} 」 minio bucket is exits!", minioProperties.getBucketName());
                }
            }
        } catch (Exception exception) {
            log.error("==> The minio connection pool check failed, please drink the configuration information！");
        }
    }

    /**
     * 上传文件
     *
     * @param uploadFile 文件流
     * @return 文件地址
     */
    @Override
    public String uploadFile(MultipartFile uploadFile) {
        String datePath = DateUtilX.format(new Date(), DateConstant.YY_MM_DD_2);
        String uuid = StrUtil.uuid();

        String originalFilename = uploadFile.getOriginalFilename();
        CommonExceptionEnum.NORMAL_EXCEPTION.isBlank(originalFilename, "文件名格式错误!");
        String fileType = originalFilename.substring(originalFilename.lastIndexOf("."), originalFilename.length());

        String fileName = "/" + datePath + "/" + uuid + fileType;
        try {
            putObject(fileName, uploadFile.getInputStream());
        } catch (Exception e) {
            throw new CommonException(e.getMessage());
        }
        return fileName;
    }

    /**
     * 下载文件
     *
     * @param fileId   文件地址
     * @param response 返回数据信息
     */
    @Override
    public void downloadFile(String fileId, HttpServletResponse response) {
        try {
            getObject(fileId, response);
        } catch (IOException e) {
            throw new CommonException(e.getMessage());
        }
    }

    @Resource
    private MinioClient minioClient;

    /**
     * 检查存储桶是否存在
     */
    @Override
    public boolean bucketExists() throws Exception {
        return minioClient.bucketExists(minioProperties.getBucketName());
    }

    /**
     * 创建桶
     */
    @Override
    public void createBucket(String bucketName) throws Exception {
        minioClient.makeBucket(bucketName);
    }

    /**
     * 通过InputStream上传对象
     *
     * @param objectName 存储桶里的对象名称
     * @param stream     要上传的流 (文件的流)
     */
    @Override
    public void putObject(String objectName, InputStream stream) throws Exception {
        if (bucketExists()) {
            minioClient.putObject(minioProperties.getBucketName(), objectName, stream, new PutObjectOptions(stream.available(), -1));
            ObjectStat statObject = statObject(objectName);
            if (statObject != null) {
                statObject.length();
            }
        }
    }

    /**
     * 删除一个对象
     *
     * @param objectName 存储桶里的对象名称
     */
    @Override
    public void removeObject(String objectName) throws Exception {
        if (bucketExists()) {
            minioClient.removeObject(minioProperties.getBucketName(), objectName);
        }
    }

    /**
     * 删除指定桶的多个文件对象,返回删除错误的对象列表，全部删除成功，返回空列表
     *
     * @param objectNames 含有要删除的多个object名称的迭代器对象
     */
    @Override
    public List<String> removeObject(List<String> objectNames) throws Exception {
        List<String> deleteErrorNames = new ArrayList<>();
        if (bucketExists()) {
            Iterable<Result<DeleteError>> results = minioClient.removeObjects(minioProperties.getBucketName(), objectNames);
            for (Result<DeleteError> result : results) {
                DeleteError error = result.get();
                deleteErrorNames.add(error.objectName());
            }
        }
        return deleteErrorNames;
    }

    /**
     * 获取对象的元数据
     *
     * @param objectName 存储桶里的对象名称
     */
    @Override
    public ObjectStat statObject(String objectName) throws Exception {
        if (bucketExists()) {
            return minioClient.statObject(minioProperties.getBucketName(), objectName);
        }
        return null;
    }

    /**
     * 文件访问路径
     *
     * @param objectName 存储桶里的对象名称
     */
    @Override
    public String getObjectUrl(String objectName) throws Exception {
        if (bucketExists()) {
            return minioClient.getObjectUrl(minioProperties.getBucketName(), objectName);
        }
        return null;
    }

    /**
     * 下载文件
     *
     * @param filename 文件名
     * @param response 输出文件
     */
    @Override
    public void getObject(String filename, HttpServletResponse response) throws IOException {
        InputStream in = null;
        OutputStream out = null;
        try {
            in = minioClient.getObject(minioProperties.getBucketName(), filename);
            int length;
            byte[] buffer = new byte[1024];
            out = response.getOutputStream();
            response.reset();
            response.addHeader(HttpHeaders.CONTENT_DISPOSITION,
                    " attachment;filename=" + URLEncoder.encode(filename, StandardCharsets.UTF_8.toString()));
            response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
            while ((length = in.read(buffer)) > 0) {
                out.write(buffer, 0, length);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (in != null) {
                in.close();
            }
            if (out != null) {
                out.close();
            }
        }
    }

}
